<!DOCTYPE html>
<html>

<head>
    <title>bar test music-visualization</title>
    <link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maptalks/dist/maptalks.css">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/maptalks/dist/maptalks.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/three@0.97.0/build/three.min.js"></script>
    <script type="text/javascript" src="../dist/maptalks.three.js"></script>
    <style>
        html,
        body {
            margin: 0px;
            height: 100%;
            width: 100%;
        }

        #map {
            width: 100%;
            height: 900px;
            background-color: #000;
        }
    </style>
</head>

<body>
    <div id="map"></div>
    <script>

        var map = new maptalks.Map("map", {
            center: [120.88083857368815, 31.494732837748273],
            zoom: 10,
            pitch: 35,
            bearing: -43.600000000000136,

            centerCross: true,
            doubleClickZoom: false,
            // baseLayer: new maptalks.TileLayer('tile', {
            //     urlTemplate: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',
            //     subdomains: ['a', 'b', 'c', 'd'],
            //     attribution: '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/">CARTO</a>'
            // })
        });

        // the ThreeLayer to draw
        var threeLayer = new maptalks.ThreeLayer('t', {
            forceRenderOnMoving: true,
            forceRenderOnRotating: true
            // animation: true
        });
        threeLayer.prepareToDraw = function (gl, scene, camera) {
            var light = new THREE.PointLight(0xffffff);
            // light.position.set(0, -10, 10).normalize();
            camera.add(light);

            var ambientlight = new THREE.AmbientLight(0x999999, 1.57);
            scene.add(ambientlight);

            var oReq = new XMLHttpRequest();
            oReq.open('GET', './data/roll-it-up.mp3', true);
            oReq.responseType = 'arraybuffer';

            oReq.onload = function (e) {
                audioContext.decodeAudioData(oReq.response, initVisualizer);
            };
            oReq.send();
            threeLayer.config('animation', true);
            animation();


        };
        threeLayer.addTo(map);


        function animation() {
            // map.setBearing(map.getBearing() - 0.08);
            requestAnimationFrame(animation);
        }


        var minLng = 120.0337719797137;
        var minLat = 30.85610622801201;
        var maxLat = 32.152846278953274;
        var maxLng = 121.63631352726838;
        var scale = 8000;
        var averageLng = (maxLng - minLng) / 50;
        var averageLat = (maxLat - minLat) / 50;
        var UPDATE_DURATION = 100;
        var AudioContext = window.AudioContext || window.webkitAudioContext;
        var audioContext = new AudioContext();
        var inited;

        // code from https://www.echartsjs.com/examples/zh/editor.html?c=bar3d-music-visualization&gl=1
        function initVisualizer(audioBuffer) {
            inited = true;

            var source = audioContext.createBufferSource();
            source.buffer = audioBuffer;

            // Must invoked right after click event
            if (source.noteOn) {
                source.noteOn(0);
            } else {
                source.start(0);
            }

            var analyzer = audioContext.createAnalyser();
            var gainNode = audioContext.createGain();
            analyzer.fftSize = 4096;

            gainNode.gain.value = 1;
            source.connect(gainNode);
            gainNode.connect(analyzer);
            analyzer.connect(audioContext.destination);

            var frequencyBinCount = analyzer.frequencyBinCount;
            var dataArray = new Uint8Array(frequencyBinCount);


            function update() {
                analyzer.getByteFrequencyData(dataArray);

                var item = [];
                var size = 50;
                var dataProvider = [];

                for (var i = 0; i < size * size; i++) {
                    var x = i % size;
                    var y = Math.floor(i / size);
                    var dx = x - size / 2;
                    var dy = y - size / 2;

                    var angle = Math.atan2(dy, dx);
                    if (angle < 0) {
                        angle = Math.PI * 2 + angle;
                    }
                    var dist = Math.sqrt(dx * dx + dy * dy);
                    var idx = Math.min(
                        frequencyBinCount - 1, Math.round(angle / Math.PI / 2 * 60 + dist * 60) + 100
                    );

                    var val = Math.pow(dataArray[idx] / 100, 3);
                    dataProvider.push([x, y, Math.max(val, 0.1)]);
                }
                var musdata = [];
                for (var i = 0; i < dataProvider.length; i++) {
                    var d = dataProvider[i];
                    var x = d[0],
                        y = d[1],
                        z = d[2];
                    var lng = minLng + x * averageLng;
                    var lat = minLat + y * averageLat;
                    var height = z * scale;
                    if (height < 2000) continue;
                    musdata.push({
                        // name: Math.random() * 10000,
                        value: [lng, lat, height]
                    });
                }
                addBars(musdata);

                setTimeout(update, UPDATE_DURATION);
            }
            update();
        }


        var bars;
        var minValue = Infinity, maxValue = -Infinity;
        function addBars(data) {
            if (bars) {
                threeLayer.removeMesh(bars);
            }
            bars = data.map(function (d) {
                var value = d.value;
                minValue = Math.min(minValue, value[2]);
                maxValue = Math.max(maxValue, value[2]);
                return threeLayer.toBar(value.slice(0, 2), {
                    height: value[2],
                    radius: 900,
                    topColor: '#fff',
                    radialSegments: 4
                }, getMaterial(value[2]));
            });
            threeLayer.addMesh(bars);
        }


        var color = ['#010103', '#2f490c', '#b0b70f', '#fdff44', '#fff'];
        // 2000.3760000000002,67917.31199999999
        function getColor(z) {
            z /= 10000;
            if (z <= 0.7) {
                return color[0];
            }
            if (z > 0.7 && z <= 1.2) {
                return color[1];
            }
            if (z > 1.2 && z <= 2) {
                return color[2];
            }
            if (z > 2 && z <= 3) {
                return color[3];
            }
            return color[4];
        }

        var materialMap = {};
        function getMaterial(z) {
            var color = getColor(z);
            var material = materialMap[color];
            if (!material) {
                material = materialMap[color] = new THREE.MeshBasicMaterial({ color: color, blending: THREE.AdditiveBlending });
            }
            return material;
        }


    </script>
</body>

</html>